using System;
using ebcpatterns.flow;
using NUnit.Framework;

namespace ebcpatterns.tests.flow
{
    [TestFixture]
    public class TripleJoinTests
    {
        [Test]
        public void Join_joines_the_three_input_messages_if_Input1_is_called_first() {
            var join = new Join<int, long, string, MyTuple<int, long, string>>();

            MyTuple<int, long, string> result = null;
            join.Output += x => result = x;
            join.Input1(42);
            join.Input2(1L);
            join.Input3("x");

            Assert.That(result.Item1, Is.EqualTo(42));
            Assert.That(result.Item2, Is.EqualTo(1L));
            Assert.That(result.Item3, Is.EqualTo("x"));
        }

        [Test]
        public void Join_joines_the_three_input_messages_if_Input2_is_called_first() {
            var join = new Join<int, long, string, MyTuple<int, long, string>>();

            MyTuple<int, long, string > result = null;
            join.Output += x => result = x;
            join.Input2(1L);
            join.Input1(42);
            join.Input3("x");

            Assert.That(result.Item1, Is.EqualTo(42));
            Assert.That(result.Item2, Is.EqualTo(1L));
            Assert.That(result.Item3, Is.EqualTo("x"));
        }

        [Test]
        public void Join_joines_the_three_input_messages_if_Input1_is_called_first_and_Input2_two_times() {
            var join = new Join<int, int, string, MyTuple<int, int, string>>();

            MyTuple<int, int, string> result = null;
            join.Output += x => result = x;
            join.Input1(1);
            join.Input2(2);
            join.Input3("x");
            join.Input2(3);

            Assert.That(result.Item1, Is.EqualTo(1));
            Assert.That(result.Item2, Is.EqualTo(3));
            Assert.That(result.Item3, Is.EqualTo("x"));
        }

        [Test]
        public void Join_joines_the_three_input_messages_if_Input2_is_called_first_and_Input1_two_times() {
            var join = new Join<int, int, int, MyTuple<int, int, int>>();

            MyTuple<int, int, int> result = null;
            join.Output += x => result = x;
            join.Input2(1);
            join.Input1(2);
            join.Input3(5);
            join.Input1(3);

            Assert.That(result.Item1, Is.EqualTo(3));
            Assert.That(result.Item2, Is.EqualTo(1));
            Assert.That(result.Item3, Is.EqualTo(5));
        }

        [Test]
        public void Join_throws_an_exception_if_target_ctor_has_no_matching_parameters() {
            var exception = Assert.Throws<InvalidOperationException>(() => new Join<int, int, int, int>());
            Assert.That(exception.Message, Is.EqualTo(
                "Type System.Int32 needs to have a constructor with three parameters of type System.Int32, System.Int32 and System.Int32 respectively."));
        }
    }

    public class MyTuple<T1, T2, T3>
    {
        public T1 Item1 { get; set; }

        public T2 Item2 { get; set; }

        public T3 Item3 { get; set; }

        public MyTuple(T1 item1, T2 item2, T3 item3) {
            Item1 = item1;
            Item2 = item2;
            Item3 = item3;
        }
    }
}